/*
 *    Copyright (c) 2018-2025, shenghua All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: shenghua
 */

package com.chen.auth.config;

import com.chen.auth.handler.AuthTokenServices;
import com.chen.auth.properties.EnableKickOutProperties;
import com.chen.auth.service.impl.AuthUserServiceImpl;
import com.chen.common.security.constant.TokenCons;
import com.chen.common.security.granter.MobileCodeTokenGranter;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.*;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 授权服务器配置
 * @author 陈锦龙
 * @since 2022/4/20 11:57
 */
@Configuration
@EnableAuthorizationServer
@AllArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final PasswordEncoder passwordEncoder;
    private final AuthenticationManager authenticationManager;
    private final AuthUserServiceImpl authUserServiceImpl;
    private final TokenStore tokenStore;
    private final DataSource dataSource;
    private final ClientDetailsService clientDetailsService;
    private final AuthTokenEnhancer authTokenEnhancer;
    private final EnableKickOutProperties enableKickOutProperties;

    /**
     * 适用密码模式所需配置
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .tokenServices(tokenServices())
                .userDetailsService(authUserServiceImpl)
                .tokenStore(tokenStore)
                .tokenEnhancer(authTokenEnhancer)
//                .exceptionTranslator(new AuthWebResponseExceptionTranslator())
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);

        List<TokenGranter> tokenGranters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
        // 添加自定义令牌授权策略
        tokenGranters.add(new MobileCodeTokenGranter(authenticationManager, endpoints));
        // 添加自定义令牌授权策略
        endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters));


//        ------------弃用JWT-----------
//        整合JWT
//        if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
//            JwtAccessTokenConverter tokenConverter = SpringContextHolder.getBean(JwtAccessTokenConverter.class);
//            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
//            List<TokenEnhancer> enhancerList = new ArrayList<>();
//            enhancerList.add(authTokenEnhancer);
//            enhancerList.add(jwtAccessTokenConverter);
//            tokenEnhancerChain.setTokenEnhancers(enhancerList);
//            // jwt
//            endpoints.tokenEnhancer(tokenEnhancerChain).accessTokenConverter(jwtAccessTokenConverter);
//        }
    }

    /**
     * 令牌管理服务
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        Boolean isKickOut = enableKickOutProperties.getIsKickOut();
        if (isKickOut){
            AuthTokenServices tokenServices = new AuthTokenServices();
            // 客户端详情
            tokenServices.setClientDetailsService(clientDetailsService);
            // 令牌存储策略
            tokenServices.setTokenStore(tokenStore);
            // 支持刷新令牌
            tokenServices.setSupportRefreshToken(true);
            // 不支持重复使用刷新令牌
            tokenServices.setReuseRefreshToken(false);
            // 令牌有效期，客户端信息配置的令牌存活时间为空时才生效
            tokenServices.setAccessTokenValiditySeconds(TokenCons.ACCESS_TOKEN_VALIDITY);
            // 刷新令牌有效期，客户端信息配置的刷新令牌存活时间为空时才生效
            tokenServices.setRefreshTokenValiditySeconds(TokenCons.REFRESH_TOKEN_VALIDITY);
            // token属性扩展
            tokenServices.setTokenEnhancer(authTokenEnhancer);
            return tokenServices;
        }

        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        // 客户端详情
        defaultTokenServices.setClientDetailsService(clientDetailsService);
        // 令牌存储策略
        defaultTokenServices.setTokenStore(tokenStore);
        // 支持刷新令牌
        defaultTokenServices.setSupportRefreshToken(true);
        // 不支持重复使用刷新令牌
        defaultTokenServices.setReuseRefreshToken(false);
        // 令牌有效期
        defaultTokenServices.setAccessTokenValiditySeconds(TokenCons.ACCESS_TOKEN_VALIDITY);
        // 刷新令牌有效期
        defaultTokenServices.setRefreshTokenValiditySeconds(TokenCons.REFRESH_TOKEN_VALIDITY);
        // token属性扩展
        defaultTokenServices.setTokenEnhancer(authTokenEnhancer);
        return defaultTokenServices;
    }

    /**
     * 配置客户端信息存储到db 代替原来得内存模式
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        JdbcClientDetailsService service = new JdbcClientDetailsService(dataSource);
        service.setPasswordEncoder(passwordEncoder);
        clients.withClientDetails(service);
    }

    /**
     * 设置认证令牌约束
     * permitAll() 表示公开
     * isAuthenticated() 表示不公开
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
                // ---oauth/token_key---
                .tokenKeyAccess("isAuthenticated()")
                // ---oauth/check_token---
                .checkTokenAccess("isAuthenticated()")
                //表单认证（申请令牌）
                .allowFormAuthenticationForClients();
    }

}
